Essential Rust Syntax Explained
Attention
For an amazing article on collections in Rust, their complexities, and when/why to use them read the docs for
std::collections
here.
if let
When extracting inner content from an enum, such as a Result
or Option
, if you're only interested in one of the variants then it's cleaner to use an if let
statement. For example,
// this function returns Result<(), Error>
let result = function_that_returns_result();
// `if let` only considers the Error variant
if let Err(e) = result {
process::exit(1);
}
// this function returns Result<(), Error>
let result = function_that_returns_result();
// `if let` only considers the Error variant
if let Err(e) = result {
process::exit(1);
}
Read the statement if let Err(e) = result
as: If result
is an Err
variant, pass the error e
to the block of code, otherwise skip it.
while let
This syntax is the looping equivalent of if let
. It will continue to loop until the while
condition fails. For example,
// returns an Option<T> with Some<T> until the stream
// is exhausted, then it returns None
let byte_stream = get_byte_stream();
while let Some(data) = byte_stream.read_next() {
// do some processing
}
// returns an Option<T> with Some<T> until the stream
// is exhausted, then it returns None
let byte_stream = get_byte_stream();
while let Some(data) = byte_stream.read_next() {
// do some processing
}
Attention
You cannot write to a variable in a loop that you don't own. Make sure to take ownership of a variable before you try to write to it in-place in a loop.
loop
Use this for an infinite loop. It is particularly useful from daemon processes.
dyn
This is used when declaring a trait as a parameter. Since the size of the object implementing the trait can't be known at compile time, it has to be cast onto the heap using Box<dyn Trait>
syntax.
.into_*
When you call a method that is prefixed with into
, such as .into_iter()
, it consumes the original object, returning the requested type, in this case, an iterator. This is distinct from the raw .iter()
call, which instead iterates over a reference, providing a view of the data without consuming it.
Rc
and Arc
These are used to make shared pointers to data (reference counted).
Cloning
- Cloning shared pointers is cheap, so don't be afraid
- Always pass a reference when cloning an
Rc
orArc
, e.g.,Rc::clone(&value)
match
You can match against two variables....
fn main() {
let pair = (5, 10);
match pair {
(0, 0) => println!("Both elements are zero"),
(_, 0) => println!("The second element is zero"),
(0, _) => println!("The first element is zero"),
(x, y) => println!("Both elements are non-zero: {}, {}", x, y),
}
}
fn main() {
let pair = (5, 10);
match pair {
(0, 0) => println!("Both elements are zero"),
(_, 0) => println!("The second element is zero"),
(0, _) => println!("The first element is zero"),
(x, y) => println!("Both elements are non-zero: {}, {}", x, y),
}
}